﻿
//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

#include "GRepProbeDlg.h"
#include "IDock.h"
#include "GbmpNew.h"
#include "IUiView.h"
#include "IElement.h"
#include "IDocument.h"
#include "IUiManager.h"
#include "IUiDocument.h"
#include "IHighlights.h"
#include "IMainWindow.h"
#include "IDockManager.h"
#include "IGraphicsNode.h"
#include "IPreHighlights.h"
#include "IElementModelShape.h"
#include "IGraphicsNodeGroup.h"
#include "IPureGraphicsElement.h"
#include "IGraphicsElementShape.h"
#include "IUiDocumentViewManager.h"
#include "IGraphicsNodeReference.h"

#include "qevent.h"
#include "QVariant"
#include "QTreeWidget.h"
#include "GRepProbe.h"
#include "GNodeWrapper.h"
#include "ObjectBrowser.h"
#include "GRepCommandIds.h"
#include "CmdGRepModuleEntry.h"
#include "BaseTreeDlg.h"
#include "GRepUiDocEventHandler.h"
#include "ICanvas.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

/// 本文件是界面实现的辅助类，不包含GDMP API的演示内容

using namespace gcmp;
using namespace Sample;

#pragma region 初始化
GrepProbeDlg::GrepProbeDlg(QWidget *parent)
    : BaseTreeDlg(parent)
    , m_selectedGNode(nullptr)
    , m_pDoc(nullptr)
    , m_opGraphicsNodeWrapper(nullptr)
{
    SetCanCheck(false);
    // 消息处理。
    connect(m_pQTreeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(OnTreeItemSelected()));
}

GrepProbeDlg::~GrepProbeDlg(void)
{
    disconnect(m_pQTreeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(OnTreeItemSelected()));
}

#pragma endregion 初始化

#pragma region 和GDMP GUI接口相关的初始化和单例获取
GrepProbeDlg* GrepProbeDlg::GetOrCreate()
{
    if (GrepProbeDlg* pPickDlg = Get())
    {
        return pPickDlg;
    }
    IUiManager *pUiManager = IUiManager::Get();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pUiManager, L"pUiManager为空",L"GDMP",L"2024-03-30");
    IDockManager * pDockManager = pUiManager->GetDockManager();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDockManager, L"pDockManager为空",L"GDMP",L"2024-03-30");
    OwnerPtr<IDock> opDock = pDockManager->CreateDock(ID_DOCK_GREP_PROBE, GBMP_TR(L"GRep探针浏览器"));
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opDock, L"opDock为空",L"GDMP",L"2024-03-30");

    OwnerPtr<GrepProbeDlg> opPickDlg = NEW_AS_OWNER_PTR(GrepProbeDlg);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opPickDlg, L"opPickDlg为空",L"GDMP",L"2024-03-30");
    opPickDlg->setMinimumWidth(200);
    opDock->SetWidget(TransferOwnership(opPickDlg));

    pDockManager->AddDock(TransferOwnership(opDock), DockArea::Right, DockOrientation::Vertical);

    return Get();
}

void Sample::GrepProbeDlg::ShowDock()
{
    IDock* pDock = GetDock(ID_DOCK_GREP_PROBE);
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDock, L"pDock为空",L"GDMP",L"2024-03-30");
    pDock->Show();
}

void Sample::GrepProbeDlg::HideDock()
{
    IDock* pDock = GetDock(ID_DOCK_GREP_PROBE);
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDock, L"pDock为空",L"GDMP",L"2024-03-30");
    pDock->Hide();
}

gcmp::ElementId Sample::GrepProbeDlg::GetElementId()
{
    if (m_opGraphicsNodeWrapper)
    {
        return m_opGraphicsNodeWrapper->GetElementId();
    }
    return ElementId::InvalidID;
}

QSize Sample::GrepProbeDlg::sizeHint() const
{
    return QSize(400, 1000);
}

// 当GRep探针浏览器中有图形节点被选中
void Sample::GrepProbeDlg::OnTreeItemSelected()
{
    IUiDocumentViewManager* pUiDocViewMgr = IUiDocumentViewManager::Get();
    IUiView* pCurrentView = pUiDocViewMgr->GetCurrentUiView();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pCurrentView, L"pCurrentView为空", L"GDMP", L"2024-03-30");
    IMainWindow* pMainWindow = IMainWindow::GetMainWindow();

    QList<QTreeWidgetItem*> selected = m_pQTreeWidget->selectedItems();

    if (nullptr == m_opGraphicsNodeWrapper || selected.count() == 0)
    {
        if (pCurrentView)
        {
            pCurrentView->GetCanvas()->Refresh();
        }
        m_selectedGNode = nullptr;
        GRepProbe* pGRepProbe = GRepProbe::Get();
        if (!GRepUiDocEventHandler::IsApplicationClosing() && pGRepProbe->IsValid())
        {
            pMainWindow->UpdateControlStatus(); // 更新界面状态，例如Ribbon菜单上按钮是否可用（ICommand::IsEnabled()）
        }
        return;
    }

    QTreeWidgetItem* item = selected.at(0);
    // 获得选中节点的IGraphicsNode_Wrapper
    QVariant qPointer = item->data(1, 0);
    IGraphicsNode_Wrapper* pGNodeWrapper = (IGraphicsNode_Wrapper*)qPointer.value<void*>();  // 获取指针
    DBG_WARN_AND_RETURN_VOID_UNLESS(pGNodeWrapper, L"pGNodeWrapper为空", L"GDMP", L"2024-03-30");
    m_selectedGNode = pGNodeWrapper->GetGraphicsNode();
    DBG_WARN_AND_RETURN_VOID_UNLESS(m_selectedGNode, L"m_selectedGNode为空", L"GDMP", L"2024-03-30");

    IPreHighlights* pPreHighlights = IPreHighlights::Get();
    pPreHighlights->Clear();
    IHighlights* pHighlights = IHighlights::Get();
    pHighlights->Clear();

    IGraphicsNodeReference& gNodeReference = pGNodeWrapper->GetGNodeReference();
    if (CmdGRepModuleEntry::GetPreviewStyle() == PreviewStyle::PreHighLight)
    {
        std::vector<OwnerPtr<IGraphicsNodeReference>> gNodeReferences;
        gNodeReferences.push_back(IGraphicsNodeReference::Create(gNodeReference));
        pPreHighlights->SetGraphicsNodeReferences(gNodeReferences);
    }
    else
    {
        pHighlights->AddGraphicsNodeReference(gNodeReference);
    }

    ObjectBrowser* pObjectBrowser = ObjectBrowser::Get();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pObjectBrowser, L"pObjectBrowser为空", L"GDMP", L"2024-03-30");
    pObjectBrowser->SetDataAndUpdate(pGNodeWrapper);
    pObjectBrowser->SetEnabled(!pGNodeWrapper->IsReadOnly());

    pCurrentView->GetCanvas()->Refresh();
    pMainWindow->UpdateControlStatus();
}

GrepProbeDlg * GrepProbeDlg::Get()
{
    IDock* pDock = GetDock(ID_DOCK_GREP_PROBE);
    if (!pDock)
    {
        return nullptr;
    }

    GrepProbeDlg* dlg = dynamic_cast<GrepProbeDlg*>(pDock->GetWidget());
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(dlg, L"dlg为空",L"GDMP",L"2024-03-30");
    return dlg;
}
#pragma endregion 和GDMP GUI接口相关的初始化和单例获取

#pragma region 和GRep相关的逻辑

void Sample::GrepProbeDlg::AddNodes(IGraphicsNode_Wrapper* pGNodeWrapper, QTreeWidgetItem* pItem, std::vector<int> location)
{
    // 设置节点显示名称
    std::wstring qTreeItemName = pGNodeWrapper->GetObjectName();
    if (location.size() > 0)
    {
        qTreeItemName += L"-" + StringUtil::ToWString(location.back());
    }
    pItem->setText(0, QString::fromStdWString(qTreeItemName));


    // 设置节点内对应的IGraphicsNode_Wrapper，例如在选择时取出IGraphicsNode_Wrapper数据显示
    QVariant qPointer = QVariant::fromValue((void *)pGNodeWrapper);   // 存放 + 生成指针
    pItem->setData(1, 0, qPointer);

    // 给节点用IGraphicsNodeReference设置唯一标识符，在GRep中节点有增加或删除等变化后，可以找到并自动选中变化前在界面上选中的节点
    std::wstring gNodeRefString = pGNodeWrapper->GetGNodeReference().GetDescriptionString();
    std::wstring locationStr = L"";
    for (auto index: location)
    {
        locationStr += L"_" + StringUtil::ToWString(index);
    }
    m_itemMap[locationStr] = pItem;
    pItem->setData(2, 0, QVariant::fromValue(QString::fromStdWString(locationStr)));
    
    // 添加子节点，例如集合图形节点的子节点，BRep图形节点中Body的Edge和Face节点，IGraphicsElementShapeReference对应的IElement的GRep节点
    int j = 0;
    for (auto& itor : pGNodeWrapper->GetChildren())
    {
        location.push_back(j);
        j++;
        QTreeWidgetItem* childItem = NEW_AS_QT(QTreeWidgetItem, pItem);
        AddNodes(itor.get(), childItem, location);
    }
}

void Sample::GrepProbeDlg::SetDisplayData(gcmp::OwnerPtr<IGraphicsNode_Wrapper> opGraphicsNodeWrapper)
{
    // 现存更新前用户选中的界面上的节点
    QTreeWidgetItem* pItem = m_pQTreeWidget->currentItem(); // 获取当前选中的item
    bool hasCurrent = false;
    std::wstring index = L"";
    if (pItem) {
        hasCurrent = true;
        index = pItem->data(2, 0).toString().toStdWString();
    }

    m_pQTreeWidget->setEnabled(false);
    m_opGraphicsNodeWrapper = TransferOwnership(opGraphicsNodeWrapper);
    m_pQTreeWidget->clear();
    m_itemMap.clear();

    // 如果为空，则清空GRep探针浏览器和GRep浏览器
    if (m_opGraphicsNodeWrapper == nullptr)
    {
        ObjectBrowser* pObjectBrowser = ObjectBrowser::Get();
        pObjectBrowser->SetDataAndUpdate(nullptr);

        QTreeWidgetItem* root = m_pQTreeWidget->invisibleRootItem();
        while (root->childCount() > 0) {
            QTreeWidgetItem* item = root->takeChild(0); // 获取第一个子节点  
            delete item; // 删除子节点  
        }
        return;
    }

    // 添加节点树
    QTreeWidgetItem *topLevelItem = NEW_AS_QT(QTreeWidgetItem, m_pQTreeWidget);
    std::vector<int> location = { 0 };
    AddNodes(m_opGraphicsNodeWrapper.get(), topLevelItem, location);
    m_pQTreeWidget->setEnabled(true);
    
    m_pQTreeWidget->expandAll();

    // 根据修改前选择的节点，如果能找到界面上对应的节点，则选择该节点
    if (hasCurrent)
    {
        auto itor = m_itemMap.find(index);
        if (itor != m_itemMap.end())
        {
            QTreeWidgetItem* pNewItem = itor->second;
            pNewItem->setSelected(true);
        }
    }
    else
    {
        QTreeWidgetItem* pTopItem = m_pQTreeWidget->topLevelItem(0);
        if (pTopItem)
        {
            // 这里有时会是野指针
            // pTopItem->setSelected(true);
        }
    }
}
#pragma endregion 和GRep相关的逻辑

